# This module contains viewpoints features: (name,value)
# This also acts as values, e.g. you can use arithmetic over features
# Either the feature has a name or it takes the name of the viewpoint
# or the name is defined by the application


import music21
import copy

class Value():
    pass

class Feature():
    def __init__(self,name=None,value=Value()):
        self.name = name
        self.value = value


class IntervalFeatureError(Exception):
    pass

class IntervalFeature(Feature):

        # Interval of pitch1 - pitch2
        def __init__(self,pitch1,pitch2,displayOctave=False):
            self.name = "interval"
            self.pitch1 = pitch1
            self.pitch2 = pitch2    
            if pitch1>pitch2:
                self.sign = "+"
            else:
                self.sign = "-"

            self.octave_interval = self.octave_interval(pitch1,pitch2)
            self.interval = self.interval(pitch1,pitch2)
            self.quality = self.quality(self.interval,pitch1,pitch2)

            # By default, do not display octave
            if displayOctave:
                octaveString = str(octave_interval)
            else:
                octaveString = ""
            self.value = self.sign + octaveString + str(self.quality)+str(self.interval)


        def octave_interval(self,pitch1,pitch2):
            midi_interval = abs(pitch1.midi - pitch2.midi)
            return midi_interval/12

        def interval(self,pitch1,pitch2):
            if pitch1<pitch2:
                low = pitch1.step
                high = pitch2.step
            elif pitch1>pitch2:
                low = pitch2.step
                high = pitch1.step
            else:
                return 1
                
            high_i = list.index(music21.pitch.STEPNAMES,high)
            interval=0
            try:
                while True:
                    if music21.pitch.STEPNAMES[high_i-interval]==low:
                        return interval+1
                    else:
                        interval +=1
            except IndexError:
                raise IntervalFeatureError("Cannot determine interval")

        def quality(self,interval,pitch1,pitch2):
            if pitch1<pitch2:
                midi_interval = (pitch2.midi - pitch1.midi)%12
            elif pitch1>pitch2:
                midi_interval = (pitch1.midi - pitch2.midi)%12
            else:
                midi_interval=0

            interval_norms = {1:0,2:2,3:4,4:5,5:7,6:9,7:11}
            deviation = midi_interval - interval_norms[interval]

            # Convert deviation to quality, depending on interval
            if interval in [1,4,5]:
                if deviation < 0:
                    "".join(map(lambda x:"d",range(abs(deviation))))
                elif deviation == 0:
                    return "P"
                elif deviation > 0:
                    "".join(map(lambda x:"A",range(abs(deviation))))
		else:
                    raise IntervalFeatureError("Cannot determine quality for %s, %s, interval:%s, deviation:%s" % (str(pitch1), str(pitch2), str(interval), str(deviation)))
            else:
                if deviation < -1:
                    "".join(map(lambda x:"d",range(abs(deviation+1))))
                elif deviation == -1:
                    return "m"
                elif deviation == 0:
                    return "M"
                elif deviation > 0:
                    "".join(map(lambda x:"A",range(abs(deviation))))
		else:
                    raise IntervalFeatureError("Cannot determine quality for %s, %s, interval:%s, deviation:%s" % (str(pitch1), str(pitch2), str(interval), str(deviation)))
                




class PitchFeature(Feature):

    def __init__(self,pitch):
        self.value = pitch
        self.name = "pitch"

    # Substraction of music21 pitch objects, returns a diatonic interval
    def __sub__(self,other):
            return IntervalFeature(self.value,other.value)

class DurationFeature(Feature):

    def __init__(self,duration):
        self.value = duration
        self.name = "duration"

    def __rdiv__(self,other):
        return Feature(str(self.name)+"/"+str(other.name),self.value/other.value)
